#include "../ui/element/Map.h"
#include "UI.h"
#include "procedure.h"
#include "netdata.h"
#include <tinycthread.h>
#include <network.h>
#include <queue>
#include <easyx.h>
#include "../thread/ABQueue.h"
#include "tool.h"
#include <map>

bool GameStarted = false;
extern int myId;
Map myMap(GAME_UI,33,33,833,633,5);
Player players[4]; //这个变量用于保存服务器发来的数据
int lastPackTick[4]; //保存每个玩家上次发包的tick
long long FireBeginTime = 0;
int FireButtonPressSecond = -1; //enter开火键是否按下
int myProjectileCount = 0;
std::map<int,Projectile> playerProjectiles[4];
std::queue<PlayerStatus> LocalPlayerTickStatus;
extern ABQueue<GamePack> PackFromServerQueue;
void updateLocalProjectilePosition();
void verifyPlayerPosition(GamePack &pack);
void scanAndHandleUserKey();

void PlayerStartGame(const bool *playerList) {
    myMap.loadFromFile("map/1.txt");
    for (int i = 0; i < 4; ++i) {
        if (!playerList[i]) {
            players[i].id = -1; //表示该玩家离线
            continue;
        }
        players[i].id = i;
        PixelXY xy{};
        switch (i) {
            case 0:
                xy = myMap.getPixelXYByPosition(0,0);
                players[i].direction = 3;
                break;
            case 1:
                xy = myMap.getPixelXYByPosition(0,19);
                players[i].direction = 3;
                break;
            case 2:
                xy = myMap.getPixelXYByPosition(14,0);
                players[i].direction = 1;
                break;
            case 3:
                xy = myMap.getPixelXYByPosition(14,19);
                players[i].direction = 1;
                break;
        }
        players[i].x = xy.x;
        players[i].y = xy.y;
        players[i].health = 3;
        players[i].baseHealth = 10;
        players[i].accumulateTime = 0;
        players[i].ammunition = 5;
    }
    RegisterServerMessageHandler(ParseAndHandleGamePackFromServer);
    SetListenKey('W');
    SetListenKey('A');
    SetListenKey('S');
    SetListenKey('D');
    SetListenKey(13/*Enter*/);
    LoadGameImage();
    ShowGameUI(myId,&myMap,players);
    //GameStarted = true;等收到服务器发来的第一个包校准tick吧
}

void PlayerGameOver() {
    GameStarted = false;
    for (int i = 0; i < 4; ++i) {
        players[i].id = -1;
        playerProjectiles[i].clear();
    }
    myProjectileCount = 0;
    PackFromServerQueue.clear();
    LocalPlayerTickStatus = std::queue<PlayerStatus>();
}

#define PLAYER_POSITION_UPDATE 1
#define PROJECTILE_CRASH 2
#define PLAYER_ACCUMULATE_POWER 3
#define PROJECTILE_SHOOT 4
#define PROJECTILE_DURABILITY_UPDATE 5
#define PLAYER_AMMUNITION_UPDATE 6
#define PLAYER_HEALTH_UPDATE 7
#define BLOCK_HEALTH_UPDATE 8
#define PLAYER_BASE_HEALTH_UPDATE 9
#define GAME_OVER 10
#define PLAYER_DISCONNECT 11
//#include <chrono>
//#include <iostream>
//
//std::chrono::high_resolution_clock::time_point last;

void TickExecuteServerPack() { //由位于主线程的ticker调用
//    auto now = std::chrono::high_resolution_clock::now();
//    auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(now - last);
//    last = now;
//    std::cout << "时间差为：" << duration.count() << " 毫秒" << std::endl;
    GamePack pack;
    PackFromServerQueue.turn();//翻转AB队列
    int projectileId = -1;
    while(PackFromServerQueue.dequeue(pack)) {
        switch (pack.packId) {
            case PLAYER_POSITION_UPDATE:
                if (lastPackTick[pack.playerId] > pack.tick && pack.dataLength != 8) {//可能是接收包出现了乱序，上一帧是重要的包则不能舍弃,数据长度为8为不可丢弃包
                    printf("出现丢包：tick:%d, x=%d, y=%d\n",pack.tick, players[pack.playerId].x,players[pack.playerId].y);
                    break;
                }
                players[pack.playerId].direction = pack.data[0]&0x0f;
                players[pack.playerId].x = (pack.data[1]&0x0f) * 100 + (pack.data[2]&0x0f) * 10 + (pack.data[3]&0x0f);
                players[pack.playerId].y = (pack.data[4]&0x0f) * 100 + (pack.data[5]&0x0f) * 10 + (pack.data[6]&0x0f);
                lastPackTick[pack.playerId] = pack.tick;
                //printf("[C](GP)tick:%d, x=%d, y=%d\n",pack.tick, players[pack.playerId].x,players[pack.playerId].y);
                verifyPlayerPosition(pack);
                break;
            case PLAYER_ACCUMULATE_POWER: //暂时不处理
                players[pack.playerId].accumulateTime = pack.data[0]&0x0f;
                break;
            case PROJECTILE_SHOOT:
                //乱序不可舍弃
                projectileId = (pack.data[0]&0x0f)*1000 + (pack.data[1]&0x0f)*100 + (pack.data[2]&0x0f)*10 + (pack.data[3]&0x0f);
                playerProjectiles[pack.playerId][projectileId] = {pack.data[11]&0x0f, GetProjectileDurability(pack.data[11]&0x0f),(pack.data[4]&0x0f) * 100 + (pack.data[5]&0x0f) * 10 + (pack.data[6]&0x0f),(pack.data[7]&0x0f) * 100 + (pack.data[8]&0x0f) * 10 + (pack.data[9]&0x0f),pack.data[10]&0x0f};
                //printf("收到炮弹发射包，炮弹id=%d x=%d y=%d\n",playerProjectiles[pack.playerId][projectileId].x,playerProjectiles[pack.playerId][projectileId].y,playerProjectiles[pack.playerId][projectileId].direction);
                myProjectileCount++;
                //更新炮弹位置 在地图上  待办
                break;
            case PROJECTILE_DURABILITY_UPDATE:
                //乱序不可舍弃
                projectileId = (pack.data[0]&0x0f)*1000 + (pack.data[1]&0x0f)*100 + (pack.data[2]&0x0f)*10 + (pack.data[3]&0x0f);
                if (pack.data[4] == '0') {
                    playerProjectiles[pack.playerId].erase(projectileId);
                    printf("擦除炮弹%d %d\n",pack.playerId,projectileId);
                } else {
                    playerProjectiles[pack.playerId][projectileId].health = pack.data[4] & 0x0f;
                }
                break;
            case PLAYER_AMMUNITION_UPDATE:
                //乱序不可舍弃
                printf("玩家%d弹药数更新为%d\n",pack.playerId,pack.data[0]&0x0f);
                players[pack.playerId].ammunition = pack.data[0]&0x0f;
                RefreshPlayerInfoUI(players);
                //验证弹药数（需要吗）
                break;
            case PLAYER_HEALTH_UPDATE:
                printf("玩家%d血量更新为%d\n",pack.playerId,pack.data[0]&0x0f);
                if (pack.data[0] == '0') {

                } else {
                    players[pack.playerId].health = pack.data[0]&0x0f;
                }
                RefreshPlayerInfoUI(players);
                break;
            case BLOCK_HEALTH_UPDATE:
                myMap.setBlockLevelByPosition((pack.data[2]&0x0f)*10+(pack.data[3]&0x0f),(pack.data[0]&0x0f)*10+(pack.data[1]&0x0f),pack.data[4]&0x0f);
                break;
            case PLAYER_BASE_HEALTH_UPDATE:
                if (pack.data[0] == '0') {
                    //执行玩家阵亡的ui更新
                } else {
                    players[pack.playerId].baseHealth = pack.data[0]&0x0f;
                }
                break;
            case GAME_OVER:
                printf("游戏结束，胜方是——玩家%d\n",pack.playerId);
                break;
            case PLAYER_DISCONNECT:
                printf("玩家%d已断开连接\n",pack.playerId);
                break;
        }
    }
    //printf("还剩%d,本次接收了%d个包,距上次%lld毫秒\n",left,pkcount,duration.count());
    //long long start = TimeMilliSecond();
    if (GameStarted)
        ReDrawMap(myMap,players,playerProjectiles); //刷新地图
    //printf("  %lld\n",TimeMilliSecond()-start);
    //计算炮弹轨迹
    updateLocalProjectilePosition();
    //读取按键状态，发包
    scanAndHandleUserKey();

}

void updateLocalProjectilePosition() {
    for (int i = 0; i < 4; ++i) {
        for (auto it = playerProjectiles[i].begin(); it != playerProjectiles[i].end(); ) {

            auto nextIt = std::next(it);
            for (int j = 0; j < GetProjectileSpeed(it->second.level); ++j) {
                it->second.x += GetMoveX(it->second.direction) * 10;
                it->second.y += GetMoveY(it->second.direction) * 10;
                //printf("[C]tick=%d 炮弹位于x=%d y=%d\n",GetTick(),it->second.x,it->second.y);
                //判断炮弹有没有出范围
                if (!myMap.contains(it->second.x,it->second.y)) {
                    playerProjectiles[i].erase(it);
                    break;
                }
            }
            it = nextIt;
        }
    }
}

void scanAndHandleUserKey() {
    Player changedMe;
    bool changed = false;
    int speed = 4;
    long long pressTime,latestPressTime = 0;
    char keys[] = {'D','W','A','S'};
    if (LocalPlayerTickStatus.empty()) {
        changedMe = players[myId];
    } else {
        changedMe = LocalPlayerTickStatus.back().playerData;
    }
    for (int i = 0; i <4 ; ++i) {
        pressTime = GetKeyPressTime(keys[i]);
        if (pressTime) {
            if (latestPressTime < pressTime) {
                latestPressTime = pressTime;
                changedMe.direction = i;
            }
            changedMe.x += speed * GetMoveX(i);
            changedMe.y += speed * GetMoveY(i);
            if (!myMap.contains(changedMe.x,changedMe.y)) {
                if (changedMe.x < myMap.getArea().left) {
                    changedMe.x = myMap.getArea().left;
                }
                if (changedMe.x > myMap.getArea().right) {
                    changedMe.x = myMap.getArea().right;
                }
                if (changedMe.y < myMap.getArea().top) {
                    changedMe.y = myMap.getArea().top;
                }
                if (changedMe.y > myMap.getArea().bottom) {
                    changedMe.y = myMap.getArea().bottom;
                }
            }
            changed = true;
        }
    }
    int tick = GetTick();
    //printf("%zu\n",LocalPlayerTickStatus.size());
    if (changed) {
        LocalPlayerTickStatus.push({tick,changedMe});
        if(!SendPlayerPositionUpdatePackToServer(myId,changedMe.x,changedMe.y,changedMe.direction,tick))
            printf("向服务器发位置更新包失败\n");
    }
    if (!FireBeginTime)
        FireBeginTime = GetKeyPressTime(13);
    if (GetKeyPressTime(13) == 0)
        FireBeginTime = 0;
    int fireButtonPressTime = 0;
    if (FireBeginTime) {
        if (FireButtonPressSecond < 0) {
            FireButtonPressSecond = 0;//开始蓄力
        } else {
            fireButtonPressTime = (TimeMilliSecond() - FireBeginTime) / 1000;
            //printf("%lld %d\n",(TimeMilliSecond() - FireBeginTime),fireButtonPressTime);
        }
        if (fireButtonPressTime > 0 && fireButtonPressTime != FireButtonPressSecond && GetProjectileConsume(fireButtonPressTime) <= players[myId].ammunition) {
            if (fireButtonPressTime > 4) {
                return;
            }
            printf("蓄力%d\n",fireButtonPressTime);
            SendPlayerAccumulatePowerPackToServer(myId,fireButtonPressTime,tick);
            FireButtonPressSecond = fireButtonPressTime;
        }
    } else {
        if (FireButtonPressSecond != -1) { //炮弹发射时向服务器发送发射包，服务器接收到该包时开始计算炮弹轨迹，客户端收到响应时也开始计算并显示炮弹轨迹，炮弹在客户端遇到障碍物或敌人后显示为消失
            if (changedMe.ammunition- GetProjectileConsume(FireButtonPressSecond) >= 0) {
                printf("x=%d y=%d level=%d,shoot\n", GetShootX(players[myId]), GetShootY(players[myId]), FireButtonPressSecond);
                SendProjectileShootPackToServer(myId, myProjectileCount, FireButtonPressSecond, GetShootX(players[myId]), GetShootY(players[myId]), changedMe.direction, tick);
                //玩家子弹减少包由服务器发来
            }
            FireButtonPressSecond = -1;
        }
    }
}

//客户端本地保存的副本用以计算最新的位置，但客户端即时显示的应当是服务器发来的位置
//客户端会为自己的状态以及炮弹的状态保存最多15帧，若某帧与服务器发回的不同，则将当前状态修改为过去出现分歧那一帧的状态
//若服务器发回的帧与当前帧相差15帧，也应退回到15帧之前
//当出现本地无权修改的帧时，比如玩家血量更新，玩家基地血量更新，我们应当修改队列首尾而不是回档
void verifyPlayerPosition(GamePack &pack) {
    if (pack.playerId == myId) {
        //printf("%zu",LocalPlayerTickStatus.size());
        while (LocalPlayerTickStatus.size() > 0) {
            PlayerStatus lastTickStatus = LocalPlayerTickStatus.front();
            Player p2 = players[pack.playerId];
            //printf("tick(%d)(%d)\n  pack: %d %d %d %d %d %d %d %d\n  local:%d %d %d %d %d %d %d %d\n",
            //       pack.tick,lastTickStatus.tick,lastTickStatus.playerData.id,lastTickStatus.playerData.direction,lastTickStatus.playerData.x,lastTickStatus.playerData.y,
            //       lastTickStatus.playerData.health,lastTickStatus.playerData.baseHealth,lastTickStatus.playerData.ammunition,lastTickStatus.playerData.accumulateTime,
            //       p2.id,p2.direction,p2.x,p2.y,p2.health,p2.baseHealth,p2.ammunition,p2.accumulateTime);
            if (pack.tick < lastTickStatus.tick) {
                if (LocalPlayerTickStatus.size() > 14) { //本地帧过快或服务器帧过慢，同步惩罚
                    LocalPlayerTickStatus = std::queue<PlayerStatus>(); //直接重置队列
                    LocalPlayerTickStatus.push({GetTick(),players[pack.playerId]});
                    printf("本地帧过快\n");
                }
                //上述if的else应该是惩罚结束后有一段本地帧被删除，那么就不需要验证，直接用服务器的数据就好
                break;
            }
            if (pack.tick == lastTickStatus.tick) {
                Player p1 = players[pack.playerId];
                Player p2 = lastTickStatus.playerData;
                if (players[pack.playerId].x != lastTickStatus.playerData.x || players[pack.playerId].y != lastTickStatus.playerData.y || players[pack.playerId].direction != lastTickStatus.playerData.direction) { //不是玩家生命更新之类的客户端无法更改的包，则说明客户端计算有误，直接惩罚
                    //printf("tick(%d)\n  pack: %d %d %d %d %d %d %d %d\n  local:%d %d %d %d %d %d %d %d\n",pack.tick,p1.id,p1.direction,p1.x,p1.y,p1.health,p1.baseHealth,p1.ammunition,p1.accumulateTime,p2.id,p2.direction,p2.x,p2.y,p2.health,p2.baseHealth,p2.ammunition,p2.accumulateTime);
                    LocalPlayerTickStatus = std::queue<PlayerStatus>(); //直接重置队列
                    LocalPlayerTickStatus.push({GetTick(),players[pack.playerId]});
                    //printf("服务器验证失败\n");
                    break;
                }
            }
            LocalPlayerTickStatus.pop();
        }
    }
}
